package za.um.td.entity.mouse;

import za.um.td.entity.Placable;
import za.um.td.entity.level.AbstractTerrain;
import za.um.td.entity.manager.LevelEntityManager;
import za.um.td.prototyping.UberTower;
import za.um.td.state.client.ClientStateNode;
import za.um.td.state.client.LevelInputStateNode;
import za.um.td.state.client.LevelInputStateNode.LevelInputState;

import com.jme.entity.Entity;
import com.jme.input.InputHandler;
import com.jme.input.MouseInput;
import com.jme.input.action.InputActionEvent;
import com.jme.input.action.MouseInputAction;
import com.jme.intersection.BoundingPickResults;
import com.jme.intersection.PickResults;
import com.jme.math.Quaternion;
import com.jme.math.Ray;
import com.jme.math.Vector2f;
import com.jme.math.Vector3f;
import com.jme.renderer.ColorRGBA;
import com.jme.scene.Circle;
import com.jme.scene.Node;
import com.jme.scene.TriMesh;
import com.jme.scene.shape.Quad;
import com.jme.scene.state.BlendState;
import com.jme.scene.state.RenderState.StateType;
import com.jme.system.DisplaySystem;
import com.jmex.terrain.util.BresenhamTerrainPicker;

public class Placer extends Entity {

	private AbstractTerrain terrain;
	private Placable placable;
	private Node spatial;

	private PickResults pr;

	public Placer(String id, AbstractTerrain terrain, Placable placable) {
		super(id);

		this.placable = placable;
		this.terrain = terrain;

		initSpatial();

		pr = new BoundingPickResults();
		pr.setCheckDistance(true);

	}

	private void initSpatial() {
		spatial = new Node("Node:" + getId());
		Vector2f gridBlockSize = AbstractTerrain.GRID_BLOCK_SIZE;

		float[] angles = { 1.5808f, 0.0f, 0.0f };

		Circle radiusCircle = new Circle("RadiusCircle", 20, placable
				.getShootRadius());
		radiusCircle.setLocalRotation(new Quaternion(angles));
		radiusCircle.setSolidColor(ColorRGBA.red);
		spatial.attachChild(radiusCircle);

		for (Vector2f placeBlock : placable.getPlaceBlocks()) {
			Quad placeBlockQuad = new Quad("Quad:" + getId(), gridBlockSize.x,
					gridBlockSize.y);

			placeBlockQuad.setLocalRotation(new Quaternion(angles));
			radiusCircle.setLocalRotation(new Quaternion(angles));

			placeBlockQuad
					.setLocalTranslation(placeBlock.x, 0.0f, placeBlock.y);

			spatial.attachChild(placeBlockQuad);

			placeBlockQuad.setSolidColor(ColorRGBA.red);
		}

		spatial.attachChild(placable.getSpatial());

		// Make the mouse's background blend with what's already there
		BlendState as = DisplaySystem.getDisplaySystem().getRenderer()
				.createBlendState();
		as.setBlendEnabled(true);
		as.setSourceFunction(BlendState.SourceFunction.SourceAlpha);
		as.setDestinationFunction(BlendState.DestinationFunction.One);
		as.setTestEnabled(true);
		as.setTestFunction(BlendState.TestFunction.GreaterThan);
		spatial.setRenderState(as);
		spatial.updateRenderState();

		setSpatial(spatial);
	}

	/**
	 * Moves the placer to the mouse position
	 * 
	 * @author koekemoere
	 */
	public class MovePlacerAction extends MouseInputAction {

		@Override
		public void performAction(InputActionEvent evt) {

			Vector2f screenPos = ClientStateNode.getInstance().getMouse()
					.getMouseManager().getMousePosition();

			Vector3f worldCoords = DisplaySystem.getDisplaySystem()
					.getWorldCoordinates(screenPos, 0);
			Vector3f worldCoords2 = DisplaySystem.getDisplaySystem()
					.getWorldCoordinates(screenPos, 1);

			Ray mouseRay = new Ray(worldCoords, worldCoords2.subtractLocal(
					worldCoords).normalizeLocal());
			
			BresenhamTerrainPicker picker = new BresenhamTerrainPicker(terrain.getSpatial());
			Vector3f loc = new Vector3f();
			picker.getTerrainIntersection(mouseRay, loc);
			loc.set(Math.round(loc.x), loc.y, Math.round(loc.z));
			spatial.setLocalTranslation(loc);
			
		}
	}

	/**
	 * Places the placable at the placer's position
	 * 
	 * @author koekemoere
	 */
	public class PlacePlacableAction extends MouseInputAction {

		@Override
		public void performAction(InputActionEvent evt) {
			// FIXME
			if (MouseInput.get().isButtonDown(0)
					&& !MouseInput.get().isButtonDown(2)
					&& terrain.isPlacable(placable)) {
				((Node) getSpatial()).detachChild(placable.getSpatial());
				placable.getSpatial().setLocalTranslation(
						getSpatial().getLocalTranslation().clone());
				placable.getSpatial().clearRenderState(StateType.Blend);
				placable.getSpatial().updateRenderState();
				LevelEntityManager.getInstance().addEntity(placable);
				terrain.setGridBlocks(placable);
				LevelInputStateNode.getInstance().setStateActive(
						LevelInputState.STOP_PLACING_PLACABLE, true);
				placable.attachController();

			} 
		}

	}

	public static class PlacerHandler extends InputHandler {

		private LevelInputStateNode inputNode = LevelInputStateNode
				.getInstance();
		private MovePlacerAction movePlacerAction = null;
		private PlacePlacableAction placePlacableAction = null;

		public void update(float tpf) {

			if (MouseInput.get().isButtonDown(2)
					&& !inputNode
							.isStateActive(LevelInputState.BUSY_PLACING_PLACABLE)) {

				inputNode.setStateActive(
						LevelInputState.START_PLACING_PLACABLE, true);
				UberTower uberTower = new UberTower("");
				LevelEntityManager.getInstance().setPlacer(
						new Placer("Placer", LevelEntityManager.getInstance()
								.getTerrain(), uberTower));
				movePlacerAction = LevelEntityManager.getInstance().getPlacer().new MovePlacerAction();
				placePlacableAction = LevelEntityManager.getInstance()
						.getPlacer().new PlacePlacableAction();
				addAction(movePlacerAction);
				addAction(placePlacableAction);
				inputNode.setStateActive(
						LevelInputState.START_PLACING_PLACABLE, false);
				inputNode.setStateActive(LevelInputState.BUSY_PLACING_PLACABLE,
						true);

			} else if (inputNode
					.isStateActive(LevelInputState.STOP_PLACING_PLACABLE)) {

				removeAction(movePlacerAction);
				removeAction(placePlacableAction);
				LevelEntityManager.getInstance().setPlacer(null);
				inputNode.setStateActive(LevelInputState.STOP_PLACING_PLACABLE,
						false);
				inputNode.setStateActive(LevelInputState.BUSY_PLACING_PLACABLE,
						false);

			}

			super.update(tpf);
		}
	}

}
